package designPatterns.com.course_design.scrapy;

import designPatterns.com.course_design.scrapy.http.request.Request;
import designPatterns.com.course_design.scrapy.http.response.Response;
import designPatterns.com.course_design.scrapy.logger.Log;
import designPatterns.com.course_design.scrapy.scheduler.QueueScheduler;
import designPatterns.com.course_design.scrapy.scheduler.Scheduler;
import designPatterns.com.course_design.scrapy.spider.RequestHandler;
import designPatterns.com.course_design.scrapy.spider.Spider;
import designPatterns.com.course_design.scrapy.util.LogInjection;
import designPatterns.com.course_design.scrapy.util.MyInvocationHandler;

import java.util.Iterator;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;

/**
 * @projectName DesignPattern
 * @className study.scrapy.Engine
 * @description
 */

public class Engine implements Runnable, RequestHandler {
    public static final int END = 0;
    public static final int RUNNING = 1;
    private int currentState = RUNNING;
    Scheduler scheduler;
    Spider spider;
    DownLoader downLoader;
    private final Queue<Request> requestQueue = new LinkedBlockingDeque<>();
    private final Queue<Request> requestQueue2 = new LinkedBlockingDeque<>();
    private final Queue<Response> responseQueue = new LinkedBlockingDeque<>();

    //线程池
    ExecutorService executorService = Executors.newFixedThreadPool(MIN_POOL_SIZE);
    //最低不能少于4个线程，否则会锁死
    private static int MIN_POOL_SIZE = 4;
    // 开启注入日志实例的注解
    @LogInjection
    private Log log;

    //返回运行状态
    public int getCurrentState() {
        return currentState;
    }

    //线程池的最小容量
    public void setMinPoolSize(int minPoolSize) {
        if (minPoolSize < 4)
            return;
        MIN_POOL_SIZE = minPoolSize;
    }

    //获取线程池的最小容量
    public int getMinPoolSize() {
        return MIN_POOL_SIZE;
    }

    //-------------------------------回调方法,由其他线程完成后返回元素给引擎-------------------------
    public void getRequestFromScheduler(Request request) {
        this.requestQueue2.add(request);
    }

    public void getRequestFromSpider(Request request) {
        log.info("接收到 request:" + request.getUrl());
        this.requestQueue.add(request);
    }

    public void getResponseFromDownLoader(Response response) {
        this.responseQueue.add(response);
    }

    //-----------------------------------------回调方法结束-----------------------------------

    public synchronized boolean signal() {
        return !this.requestQueue.isEmpty() || !this.requestQueue2.isEmpty() || !this.responseQueue.isEmpty();
    }

    public boolean isRunning() {
        return this.currentState == Engine.RUNNING;
    }

    //初始化爬取url
    public void init(List<Request> requests) {
        Iterator<Request> it = requests.iterator();
        while (it.hasNext()) {
            scheduler.addRequest(it.next());
        }
    }

    /**
     * 1.若有response,则解析页面，返回数据和新的request给engine<br>
     * 2.若有request,则返回给engine作为request2,预备给downloader<br>
     * 3.若有request,则发出请求，获取页面，返回给engine<br>
     */
    public void start() {
        executorService.submit(this.spider);
        executorService.submit(this.scheduler);
        executorService.submit(this.downLoader);
        executorService.submit(this);
    }

    /**
     * 引擎<br>
     * 1.若有request，则发送至调度器 <br>
     * 2.若有request2,则发送至downloader<br>
     * 3.若有response,则发送至spider<br>
     * 引擎提交请求，其余节点在上面的代码中各自开了一个线程，用来处理请求和返回数据给引擎<br>
     * schedule添加request<br>
     */
    @Override
    public void run() {
        while (this.signal() || scheduler.signal() || spider.signal() || downLoader.signal()) {
//            log.info("轮询各个队列,此时各个队列的大小分别为: scheduler.size = " + scheduler.getCount()
//                    + ", requestQueue.size = " + requestQueue.size() + ", responseQueue.size = " + responseQueue.size());
            //与scheduler
            if (!requestQueue.isEmpty()) {
                Request request = requestQueue.poll();
                this.scheduler.addRequest(request);
            }
            //与downloader
            if (!requestQueue2.isEmpty()) {
                Request request = requestQueue2.poll();
                this.downLoader.addRequest(request);
            }
            //与spider
            if (!responseQueue.isEmpty()) {
                Response response = this.responseQueue.poll();
                this.spider.addResponse(response);
            }
            //如果队列不为空，则唤醒使其重新进入运行态,竞争锁
            this.signalWhenQueueNotEmpty();
//            this.signalWhenConditionSatisfied();
        }
        try {
            Thread.sleep(1000);
            System.out.println("==========  check  ===========");
            if (!(this.signal() || scheduler.signal() || spider.signal() || downLoader.signal())) {
                currentState = Engine.END;
                System.out.println("=====================END OF ENGINE========================");
                this.signalWhenConditionSatisfied();
                executorService.shutdown();
            } else
                run();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    //所有队列都空时，都在等待状态，此时唤醒，然后用if判断来退出while循环，结束线程
    private void signalWhenConditionSatisfied() {
//        log.info("唤醒等待的线程");
        //所有线程都在wait且state为END
        this.scheduler.lock();
        try {
            this.scheduler.signalCondition();
        } finally {
            this.scheduler.unlock();
        }
        this.downLoader.lock();
        try {
            this.downLoader.signalCondition();
        } finally {
            this.downLoader.unlock();
        }
        this.spider.lock();
        try {
            this.spider.signalCondition();
        } finally {
            this.spider.unlock();
        }
    }

    //如果队列不为空，则唤醒使其重新进入运行态
    //signalWhenConditionSatisfied方法也可以使用在
    //run中的while循环体中，但是本方法的意图在于
    //在没必要的情况下不去唤醒对方，即使对方有保护性暂停
    private void signalWhenQueueNotEmpty() {
        if (!this.scheduler.isEmpty()) {
            this.scheduler.lock();
            try {
                this.scheduler.signalCondition();
            } finally {
                this.scheduler.unlock();
            }
        }
        if (!this.downLoader.isEmpty()) {
            this.downLoader.lock();
            try {
                this.downLoader.signalCondition();
            } finally {
                this.downLoader.unlock();
            }
        }
        if (!this.spider.isEmpty()) {
            this.spider.lock();
            try {
                this.spider.signalCondition();
            } finally {
                this.spider.unlock();
            }
        }
    }

    //该层的职责是接收request，将request放入请求队列中，以便后续处理
    @Override
    public void push(Request request) {
        this.getRequestFromSpider(request);
    }

    @Override
    public void setNextHandler(RequestHandler handler) {
    }

    public Engine(Builder builder) {
        //绑定同事类
        this.scheduler = builder.scheduler;
        this.spider = builder.spider;
        this.downLoader = builder.downLoader;
    }

    /**
     * <strong>
     * 静态内部类<br>
     * 使用建造者模式来给出实例，JScrapy的创建过程涉及到中介者与同事类的相互绑定，<br>
     * 责任链的建立和使用注解标记的对象注入，具体构建过程交给建造者，简化客户端实例化流程<br></strong>
     */
    public static class Builder {
        private Scheduler scheduler;
        private Spider spider;
        private DownLoader downLoader;
        //注册器类
        RequestHandlerRegistry registry = new RequestHandlerRegistry();

        public Builder() {
            this.scheduler = null;
            this.spider = null;
            this.downLoader = null;
        }

        public static Builder newInstance() {
            return new Builder();
        }

        public Builder setScheduler(Scheduler scheduler) {
            this.scheduler = scheduler;
            return this;
        }

        public Builder setSpider(Spider spider) {
            this.spider = spider;
            return this;
        }

        public Builder setDownloader(DownLoader downLoader) {
            this.downLoader = downLoader;
            return this;
        }

        public Builder addRequestHandler(RequestHandler handler) {
            this.registry.addRequestHandler(handler);
            return this;
        }

        public Engine build() throws NoSuchFieldException, IllegalAccessException {
            //为了更佳的体现框架思想，提供了默认的downloader和scheduler
            //用户只需要注入自定义的spider抽象实现类即可
            if (this.spider == null) {
                throw new NoSuchFieldException("用户必须提供一个爬虫实例来实现抓取页面的方法");
            }
            if (this.downLoader == null) this.downLoader = new DownLoader();
            if (this.scheduler == null) this.scheduler = new QueueScheduler();
            //使用Engine的builder来实例化Engine，实例化的同时也绑定好了所有的同事类
            Engine instance = new Engine(this);
            //同事类绑定中介者
            this.spider.setEngine(instance);
            this.downLoader.setEngine(instance);
            this.scheduler.setEngine(instance);
            //首尾添加责任链的起点和终点  最后调用构建责任链方法
            this.registry.addRequestHandler(0, this.spider);
            this.registry.addRequestHandler(instance);
            this.registry.buildRequestHandlerChain();
            //使用反射和注解来注入日志实例
            MyInvocationHandler.wireLog(instance);
            MyInvocationHandler.wireLog(this.spider);
            MyInvocationHandler.wireLog(this.downLoader);
            MyInvocationHandler.wireLog(this.scheduler);
            //返回实例
            return instance;
        }
    }
}

//    public void signalIfErrorWaiting(){
//        if(this.scheduler.errorWait()){
//            this.scheduler.lock();
//            this.scheduler.signalCondition();
//            this.scheduler.unlock();
//        }
//        if(this.spider.errorWait()){
//            this.spider.lock();
//            this.spider.signalCondition();
//            this.spider.unlock();
//        }
//        if(this.downLoader.errorWait()){
//            this.downLoader.lock();
//            this.downLoader.signalCondition();
//            this.downLoader.unlock();
//        }
//    }


//-------------------------------------外部注入------------------------------------
//    public void setScheduler(Scheduler scheduler) {
//        this.scheduler = scheduler;
//    }
//
//    public void setSpider(Spider spider) {
//        this.spider = spider;
//    }
//
//    public void setDownLoader(DownLoader downLoader) {
//        this.downLoader = downLoader;
//    }
//-------------------------------------注入结束------------------------------------